# Unified Debug/Release build and pack script for LocalDnsService
# 说明：脚本提供两种任务模式（Debug=调试，Release=发行）。
# - Debug 模式：检查是否运行 → 可选清理 → 可选还原 → 编译（Debug）→ 发布（不带运行时）→ 运行
# - Release 模式：检查是否运行 → 可选清理 → 可选还原 → 编译（Release）→ 发布（不带运行时与带运行时）→ 生成版本文件
# 注意：所有函数与变量均提供注释，方法名不含连字符以避免 PowerShell 解析问题。
# 编码设置已移至 param 之后，避免打破脚本参数解析

param(
    # 任务模式：Debug（调试）或 Release（发行），默认 Debug
    [ValidateSet("Debug","Release")]
    [string]$Task = "Debug",
    # 是否在编译前执行 dotnet clean
    [switch]$Clean,
    # 是否在编译前执行 dotnet restore
    [switch]$Restore
)
 
# 编码设置：统一为 UTF-8，避免乱码
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding  = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
$PSDefaultParameterValues['Out-File:Encoding']    = 'utf8'
$PSDefaultParameterValues['Set-Content:Encoding'] = 'utf8'
$PSDefaultParameterValues['Add-Content:Encoding'] = 'utf8'

# 全局错误策略：遇到错误直接停止
$ErrorActionPreference = "Stop"

# 日志辅助函数
function Write-Info($msg) { Write-Host $msg -ForegroundColor Cyan }
function Write-Ok($msg)   { Write-Host $msg -ForegroundColor Green }
function Write-Warn($msg) { Write-Host $msg -ForegroundColor Yellow }
function Write-Err($msg)  { Write-Host $msg -ForegroundColor Red }

# 项目与配置通过自动检测设置（见下方 Auto-detect project and metadata）

# 进入脚本所在目录，确保相对路径正确
Set-Location -Path (Split-Path -Parent $MyInvocation.MyCommand.Definition)

# Auto-detect project and metadata
$DebugConfiguration = "Debug"
$ReleaseConfiguration = "Release"


$csprojs = Get-ChildItem -Path . -Filter *.csproj -File -Recurse
if ($csprojs.Count -eq 0) {
    Write-Err "No .csproj found in current directory."
    exit 1
}
$folderName = Split-Path -Leaf (Get-Location)
$Project = ($csprojs | Where-Object { $_.BaseName -eq $folderName } | Select-Object -First 1).Name
if (-not $Project) { $Project = $csprojs[0].Name }

$csprojContent = Get-Content -Path $Project -Raw
$AppName = [regex]::Match($csprojContent, '<AssemblyName>(.*?)</AssemblyName>').Groups[1].Value
if ([string]::IsNullOrWhiteSpace($AppName)) { $AppName = [regex]::Match($csprojContent, '<RootNamespace>(.*?)</RootNamespace>').Groups[1].Value }
if ([string]::IsNullOrWhiteSpace($AppName)) { $AppName = [System.IO.Path]::GetFileNameWithoutExtension($Project) }




# 预检查：项目文件存在性
if (-not (Test-Path $Project)) {
    Write-Err "Project file not found: $Project"
    exit 1
}

# 函数：检查并终止正在运行的相关进程，避免占用文件或端口
function StopRunningProcesses {
    Write-Info "Checking and stopping LocalDnsService process..."
    $p = Get-Process -Name "LocalDnsService" -ErrorAction SilentlyContinue
    if ($p) {
        Write-Warn "LocalDnsService process found, terminating..."
        $p.Kill(); $p.WaitForExit(); Start-Sleep -Seconds 1
    } else {
        Write-Ok "No LocalDnsService process found"
    }

    Write-Info "Checking and stopping related dotnet processes..."
    Get-Process -Name "dotnet" -ErrorAction SilentlyContinue |
        Where-Object { $_.MainWindowTitle -like "*LocalDnsService*" } |
        Stop-Process -Force -ErrorAction SilentlyContinue

    $p2 = Get-Process -Name "LocalDnsService" -ErrorAction SilentlyContinue
    if (-not $p2) { Write-Ok "Confirm: LocalDnsService is stopped or not running" }
}

# 函数：复制配置文件到目标目录（Conf 子目录）
function CopyConfFiles($targetDir) {
    # 创建Conf子目录
    New-Item -Path (Join-Path $targetDir "Conf") -ItemType Directory -Force | Out-Null
    
    # 复制所有配置文件
    $configFiles = @(
        "Conf\DNSService.conf",
        "Conf\dns.conf",
        "Conf\dns_reverse.conf",
        "Conf\upstream_domains.conf"
    )
    
    foreach ($file in $configFiles) {
        if (Test-Path $file) {
            Copy-Item -Path $file -Destination (Join-Path $targetDir "Conf") -Force -ErrorAction SilentlyContinue
            Write-Info "已复制配置文件: $file -> $(Join-Path $targetDir "Conf")"
        }
    }
}



# 函数：清理项目指定配置（执行 dotnet clean）
function CleanProject($configuration) {
    Write-Info "dotnet clean ($configuration)"
    & dotnet clean $Project -c $configuration | Out-Host
}

# 函数：还原项目依赖（执行 dotnet restore）
function RestoreProject {
    Write-Info "dotnet restore"
    & dotnet restore $Project | Out-Host
}

# BuildProject函数已移除，编译功能现在由dotnet publish命令负责

# 函数：发布为框架依赖（不带运行时，自包含为 false）
function PublishFrameworkDependent($configuration, $outputDir) {
    Write-Info "Publish framework-dependent to: $outputDir"
    New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
    
    # 清理可能导致冲突的旧文件
    Write-Info "清理可能导致冲突的构建文件..."
    if (Test-Path "obj\$configuration") {
        Remove-Item -Path "obj\$configuration" -Recurse -Force | Out-Null
    }
    
    # 发布主项目 - 由publish负责编译，移除--no-build参数
    & dotnet publish $Project -c $configuration -o $outputDir --no-self-contained | Out-Host

    # 发布并打包 CLI 与 UI 到安装包目录（分别置于子目录，避免文件冲突）
    $cliProj = Join-Path $PSScriptRoot "LocalDns.CLI\LocalDns.CLI.csproj"
    $uiProj  = Join-Path $PSScriptRoot "LocalDns.UI\LocalDns.UI.csproj"

    if (Test-Path $cliProj) {
        Write-Info "Publish CLI (no runtime) to: $outNoRtCli"
        & dotnet publish $cliProj -c $configuration -o $outputDir --no-self-contained | Out-Host
    }
    if (Test-Path $uiProj) {
        Write-Info "Publish UI (no runtime) to: $outNoRtUi"
        & dotnet publish $uiProj -c $configuration -o $outputDir --no-self-contained | Out-Host
    }
    CopyConfFiles $outputDir
}

# 函数：发布为自包含（带运行时，win-x64）
function PublishSelfContained($configuration, $outputDir) {
    Write-Info "Publish self-contained (win-x64) to: $outputDir"
    # 确保为 win-x64 执行 RID 特定的 restore
    & dotnet restore $Project -r win-x64 | Out-Host
    New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
    # 取消 --no-build，以便针对 win-x64 进行编译与发布
    if ($configuration -eq $ReleaseConfiguration) {
        & dotnet publish $Project -c $configuration -o $outputDir --self-contained -r win-x64 | Out-Host
        
        # 发布并打包 CLI 与 UI 到安装包目录（分别置于子目录，避免文件冲突）
        $cliProj = Join-Path $PSScriptRoot "LocalDns.CLI\LocalDns.CLI.csproj"
        $uiProj  = Join-Path $PSScriptRoot "LocalDns.UI\LocalDns.UI.csproj"

        # 自包含版（带运行时）
        if (Test-Path $cliProj) {
            Write-Info "Publish CLI (with runtime) to: $outputDir"
            & dotnet publish $cliProj -c $ReleaseConfiguration -o $outputDir --self-contained -r win-x64 | Out-Host
        }
        if (Test-Path $uiProj) {
            Write-Info "Publish UI (with runtime) to: $outputDir"
            & dotnet publish $uiProj -c $ReleaseConfiguration -o $outputDir --self-contained -r win-x64 | Out-Host
        }
    } else {
        # 在Debug模式下移除GenerateAssemblyInfo相关参数
        & dotnet publish $Project -c $configuration -o $outputDir --self-contained -r win-x64 | Out-Host
    }
    CopyConfFiles $outputDir
}

# 函数：运行应用（从发布输出目录启动 EXE，非阻塞）
function RunApplication($exePath) {
    if (Test-Path $exePath) {
        Write-Info "Starting app: $exePath (non-blocking)"
        Start-Process -FilePath $exePath | Out-Null
        Write-Ok "App started. Note: Admin privileges may be required to bind port 53."
    } else {
        Write-Warn "Executable not found: $exePath; skip run"
    }
}

# 等待EXE生成完成（发布后有时磁盘刷新存在延迟）
function WaitForExecutableReady([string]$exePath, [int]$timeoutSec = 60) {
    Write-Info "等待应用程序编译完成: $exePath"
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    while (-not (Test-Path $exePath)) {
        if ($sw.Elapsed.TotalSeconds -ge $timeoutSec) {
            Write-Err "未检测到已编译的可执行文件: $exePath"
            throw "EXE未就绪: $exePath"
        }
        Start-Sleep -Seconds 1
    }
    Write-Ok "EXE就绪: $exePath"
}

# 主流程入口
Write-Info "Task: $Task (default Debug), Clean=$($Clean.IsPresent), Restore=$($Restore.IsPresent)"
StopRunningProcesses

if ($Task -eq "Debug") {
    # 调试流程：清理 → 还原 → 发布（Debug，框架依赖，由publish负责编译）→ 运行
    if ($Clean) { CleanProject $DebugConfiguration }
    if ($Restore) { RestoreProject }

    $debugOutDir = Join-Path -Path ("bin\" + $DebugConfiguration + "\net8.0") -ChildPath "win-x64"
    PublishFrameworkDependent $DebugConfiguration $debugOutDir

    $debugExe = Join-Path $debugOutDir ($AppName + ".exe")
    RunApplication $debugExe

    Write-Ok "Debug task done. Output: $debugOutDir"
}
elseif ($Task -eq "Release") {
    # 发行流程：清理 → 还原 → 发布两版（由publish负责编译）→ 等待EXE → 生成版本文件/安装包
    # 发行模式下强制清理，确保生成干净的发布包
    CleanProject $ReleaseConfiguration
    # 发行模式下强制还原，确保所有依赖都是最新的
    RestoreProject

    $outNoRuntime = Join-Path -Path ("bin\" + $ReleaseConfiguration + "\net8.0") -ChildPath "win-x64-no-runtime"
    $outWithRuntime = Join-Path -Path ("bin\" + $ReleaseConfiguration + "\net8.0") -ChildPath "win-x64-with-runtime"
    
    # 清理发布目录，确保没有旧文件残留
    Write-Info "清理发布目录..."
    if (Test-Path $outNoRuntime) { 
        Write-Info "删除目录: $outNoRuntime"
        Remove-Item -Path $outNoRuntime -Recurse -Force 
    }
    if (Test-Path $outWithRuntime) { 
        Write-Info "删除目录: $outWithRuntime"
        Remove-Item -Path $outWithRuntime -Recurse -Force 
    }

    PublishFrameworkDependent $ReleaseConfiguration $outNoRuntime
    PublishSelfContained $ReleaseConfiguration $outWithRuntime


    # 显式等待两个EXE准备就绪
    $exeNoRuntime  = Join-Path $outNoRuntime  ($AppName + ".exe")
    $exeWithRuntime = Join-Path $outWithRuntime ($AppName + ".exe")
    WaitForExecutableReady $exeNoRuntime  60
    WaitForExecutableReady $exeWithRuntime 60

    # 生成安装包（如果已安装 Inno Setup）
    $installerScript = Join-Path $PSScriptRoot "installer.ps1"
    if (Test-Path $installerScript) {
        Write-Info "Invoking installer builder script: $installerScript (AutoInstall Silent)"
        & powershell -ExecutionPolicy Bypass -File $installerScript -AutoInstall -SilentInstall | Out-Host
    } else {
        Write-Warn "installer.ps1 not found; skip installer build."
    }

    Write-Ok "Release task done."
}
else {
    Write-Warn "Unknown task: $Task" 
}

Write-Ok "All done."